# -*- coding: utf-8 -*-
from openerp.osv import fields
from openerp.osv import osv
from datetime import datetime, timedelta
import openerp.addons.decimal_precision as dp
import pytz
import time, calendar
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
import openerp.tools as tools
from openerp import  fields as field_new
from openerp import models, api, _, tools
from openerp.addons.wechat.enterprise import WxApi
from openerp import SUPERUSER_ID
from openerp.tools.float_utils import float_compare, float_round, float_is_zero
from openerp.addons.dftg_weixin.application.application_weixin_contact import WxAppContact
import os
import datetime as currency_data

class stock_return_picking(osv.osv_memory):
    _inherit = "stock.return.picking"

    def check_return_qty(self, cr, uid, ids, context=None):
        picking_qty_dict = {}
        return_qty_dict = {}
        # 用字典 picking_qty_dict记录 源单据中的产品的 产品 数量
        for picking in self.pool.get('stock.picking').browse(cr, uid, ids, context=context):
            for move in picking.move_lines:
                picking_qty_dict.update(
                    {move.product_id.id: move.product_uom_qty + picking_qty_dict.get(move.product_id.id, 0)})
        # 用字典 return_qty_dict 所有源单 的退货单 的  产品 数量
        picking_id = self.pool.get('stock.picking').search(cr, uid, [('origin_picking_id', '=', picking.id),
                                                                ('state', '!=', 'cancel')], context=context)
        for picking in self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context):
            for move in picking.move_lines:
                return_qty_dict.update(
                    {move.product_id.id: move.product_uom_qty + return_qty_dict.get(move.product_id.id, 0)})
        # 核对 源单 和退货 货单的 产品的产品的数量 是否是一致的 。（退货单的产品必须在源单中出现
        # ，y退货的数量必须比源单中的产品的数量小）
        if list(set(return_qty_dict.keys())-set(picking_qty_dict.keys())):
            raise osv.except_osv(u'错误', u"您所退货的产品不在源单据内!")
        for product_id in return_qty_dict.keys():
            if return_qty_dict.get(product_id) > picking_qty_dict.get(product_id):
                raise osv.except_osv(u'错误', u"您所要退货的产品的数量比源单据中的数量还要大，请核对后再进行操作!")
        return True

    def _create_returns(self, cr, uid, ids, context=None):
        # 添加了退货单的 判断逻辑，退货单不能够再进行退货
        return_vals = super(stock_return_picking, self)._create_returns(
            cr, uid, ids, context=context)
        stock_picking = self.pool.get('stock.picking').browse(
            cr, uid, [context.get('active_id')], context=context)
        self.check_return_qty(cr,uid,context.get('active_id'),context=context)

        if stock_picking.origin_picking_id:
            raise osv.except_osv(u'错误', u"不允许对退货单进行退货操作!")
        if return_vals[0]:
            stock_picking_obj = self.pool.get('stock.picking').browse(
                cr, uid, return_vals[0], context=context)
            stock_picking_obj.write(
                {'origin_picking_id': context.get('active_id')})
            if stock_picking.origin and stock_picking.so_id:  # and stock_picking.picking_type_code == 'outgoing'
                # 销售订单生成的出库单的 退货操作
                for stock_move_line in stock_picking_obj.move_lines:
                    sale_order_line_id = self.pool.get("sale.order.line").search(cr, uid, [(
                        "order_id", "=", stock_picking.so_id.id), ("product_id", "=", stock_move_line.product_id.id)], context=context)
                    sale_order_lines = self.pool.get("sale.order.line").browse(
                        cr, uid, sale_order_line_id, context=context)
                    # 生成退货单时 减少生销售订单上生成 出库单的数量（如果销售订单上已有已出库数量 则在 生成的这张入库但移动后
                    # 减少）
                    sale_order_lines.write({"delivered_qty": sale_order_lines.delivered_qty - stock_move_line.product_uom_qty,
                                            'is_shipment_completion': False})

            if stock_picking.origin and stock_picking.origin[0] == 'P':  # and stock_picking.picking_type_code == 'incoming'
                for stock_move_line in stock_picking_obj.move_lines:
                    purchase_order_line_id = self.pool.get("purchase.order.line").search(cr, uid, [(
                        "order_id", "=", stock_picking.po_id.id), ("product_id", "=", stock_move_line.product_id.id),('prod_spec','=',stock_move_line.prod_spec)], context=context)
                    purchase_order_lines = self.pool.get("purchase.order.line").browse(
                        cr, uid, purchase_order_line_id, context=context)
                    # 生成退货单时 减少生销售订单上生成 入库单的数量（如果采购订单上已有已出库数量 则在生成的这张入库单移动后减少）
                    purchase_order_lines.write({"delivered_qty": purchase_order_lines.delivered_qty - stock_move_line.product_uom_qty,
                                                })
        return return_vals


class picture_list(osv.osv):
    _name = 'picture.list'

    def get_image(self, cr, uid, path, context=None):
        image_filed = None
        try:
            with open(os.path.join(tools.config.filestore(cr.dbname), path), 'rb') as open_file:
                image_filed = open_file.read()
                open_file.close()
        except IOError as e:
            pass
        return image_filed

    def _get_image_sign(self, cr, uid, ids, name, args, context=None):
        res = {}
        for picking_row in self.browse(cr, uid, ids, context=context):
            res[picking_row.id] = picking_row.image_sign_path and self.get_image(cr, uid, picking_row.image_sign_path,
                                                                                 context=context)
        return res

    _columns = {
        'image_sign': fields.function(_get_image_sign, type='binary', string=u"签收图片", help=u"通过微信发送的出库单签收照片"),
        'image_sign_path': fields.char(u"签收图片路径", help=u"通过微信发送的出库单签收照片"),
        'picking_id': fields.many2one('stock.picking', string='出入库单')
    }


class stock_picking(osv.osv):
    _name = 'stock.picking'
    _inherit = ['stock.picking', 'china.city.county']

    @api.cr_uid_ids_context
    def do_enter_transfer_details(self, cr, uid, picking, context=None):
        for picking_row in self.browse(cr, uid, picking, context=context):
            context.update({'picking_type_code': True if picking_row.picking_type_code =='incoming' else False})
        return super(stock_picking, self).do_enter_transfer_details(cr, uid, picking, context=context)

    def redraft(self, cr, uid, ids, context=None):
        for picking in self.browse(cr, uid, ids, context=context):
            for line in picking.move_lines:
                line.write({'state': 'draft'})

    def action_cancel(self, cr, uid, ids, context=None):
        # 因为出库单是和销售订单 和采购单进行关联的
        # 采购单、销售订单上都有已建 出入库单的字段 这个字段是根据有效的单据进行填写的】
        # 如果删除或者取消就要修改已有单据上的数据进行同步
        # 收货单取消所进行的操作，写回销售订单行中的已发货数据
        super(stock_picking, self).action_cancel(cr, uid, ids, context=context)
        sale_order_obj = self.pool.get("sale.order")
        purchase_order_obj = self.pool.get("purchase.order")
        sale_order_line_obj = self.pool.get("sale.order.line")
        purchase_order_line_obj = self.pool.get("purchase.order.line")
        for stock_picking_row in self.pool.get("stock.picking").browse(cr, uid, ids, context=context):
            if not stock_picking_row.origin:
                continue
            # 销售订单生成出库单的取消时的操作
            if stock_picking_row.so_id and (stock_picking_row.so_id.is_return or stock_picking_row.so_id.is_exchange):
                context = {'can_cancel': True}.update(context or {})
                model_data_obj = self.pool.get('ir.model.data')
                card_exchange_order_id = model_data_obj.xmlid_to_res_id(
                    cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
                if stock_picking_row.so_id.state != 'cancel' and \
                                stock_picking_row.so_id.order_type_id.id != card_exchange_order_id\
                        and stock_picking_row.picking_type_code == 'incoming':
                    stock_picking_row.so_id.action_cancel()

            if stock_picking_row.so_id and stock_picking_row.picking_type_code.find('outgoing') >= 0:
                sale_order_id = sale_order_obj.search(
                    cr, uid, [("name", "=", stock_picking_row.origin)], context=context)
                is_shipment_completion = True
                if sale_order_id:
                    for stock_move_line in stock_picking_row.move_lines:
                        if is_shipment_completion and stock_move_line.product_uom_qty > 0:
                            is_shipment_completion = False
                        sale_order_line_id = sale_order_line_obj.search(cr, uid, [(
                            "order_id", "=", sale_order_id),
                            ("product_id", "=", stock_move_line.product_id.id),
                            ("product_uom_qty", ">", 0)
                        ], context=context)
                        sale_order_lines = sale_order_line_obj.browse(
                            cr, uid, sale_order_line_id, context=context)
                        sale_order_lines.write({"delivered_qty": sale_order_lines.delivered_qty - stock_move_line.product_uom_qty})
                if not is_shipment_completion:
                    sale_order_obj.write(cr, uid, sale_order_id, {
                                         "shipment_completion": False}, context=context)
            # 销售退货单
            if stock_picking_row.so_id and stock_picking_row.picking_type_code.find('incoming') >= 0:  # (stock_pickings.origin)[0] == 'W'
                # 销售订单生成出库单的  退货单 取消时的操作
                for stock_move_line in stock_picking_row.move_lines:
                    # 防止重复发货， 一个销售订单已发 3个退一个  然后再补发一个，如果 退货的那个入库单取消（退货的没有入库）， 就相当与多发了一个。
                    if stock_picking_row.origin_picking_id and \
                        stock_move_line.so_line_id.delivered_qty + stock_move_line.product_uom_qty >\
                                    abs( stock_move_line.so_line_id.product_uom_qty):
                        raise osv.except_osv(u'错误', u"不能取消，因为已发货数量超出了订单行数量")
                    stock_move_line.so_line_id.write({"delivered_qty":  stock_move_line.so_line_id.delivered_qty\
                                                                        - stock_move_line.product_uom_qty})
            # 采购单入库的
            if (stock_picking_row.origin)[0] == 'P':
                purchase_order_id = purchase_order_obj.search(
                    cr, uid, [("name", "=", stock_picking_row.origin)], context=context)
                is_shipped = True
                for stock_move_line in stock_picking_row.move_lines:
                    if is_shipped and stock_move_line.product_uom_qty > 0:
                        is_shipped = False
                    purchase_order_line_id = purchase_order_line_obj.search(cr, uid, [(
                        "order_id", "=", purchase_order_id), ("product_id", "=", stock_move_line.product_id.id), ('prod_spec', '=', stock_move_line.prod_spec)], context=context)
                    purchase_order_lines = purchase_order_line_obj.browse(
                        cr, uid, purchase_order_line_id, context=context)
                    purchase_order_lines.write({
                        "delivered_qty": purchase_order_lines.delivered_qty - stock_move_line.product_uom_qty
                    })

                if not is_shipped:
                    purchase_order_obj.write(cr, uid, purchase_order_id, {
                                             "pick_state": False}, context=context)
            # 采购退货单
            if stock_picking_row.origin_picking_id and (stock_picking_row.origin)[0] != 'P' and stock_picking_row.po_id:
                for stock_move_line in stock_picking_row.move_lines:
                    # 防止重复发货， 一个销售订单已发 3个退一个  然后再补发一个，
                    # 如果 退货的那个入库单取消（退货的没有入库）， 就相当与多发了一个。
                    purchase_line_row = stock_move_line.purchase_line_id
                    if not stock_picking_row.po_id.is_return and purchase_line_row.delivered_qty +\
                        stock_move_line.product_uom_qty > abs(purchase_line_row.product_qty):
                        raise osv.except_osv(u'错误', u"不能取消，因为已发货数量超出了订单行数量")
                    purchase_line_row.write({"delivered_qty": purchase_line_row.delivered_qty -
                                                              stock_move_line.product_uom_qty})

        return True

    def onchange_location_id(self, cr, uid, ids, source_location_id, dest_location_id, context=None):
        if source_location_id and dest_location_id:
            if source_location_id == dest_location_id:
                return {'value': {'dest_location_id': False},
                        'warning': {'title': u'警告', 'message': u'目的库位和源库位不能相等！'}
                        }
        return True

    def remind_delivery(self, cr, uid, receive_uid=False):
        """每天定时给销售发送明天要发货的通知 货的数量是否备齐"""
        if tools.config.get('is_production'):
            sale_obj = self.pool.get('sale.order')
            tomrrow_date = (datetime.now() + timedelta(1)).strftime(DEFAULT_SERVER_DATE_FORMAT)
            oder_ids = sale_obj.search(cr, uid, [('expected_delivery_time', '=', tomrrow_date),
                                                 ('state', 'not in', ('done', 'draft', 'cancel')),
                                                 ('apply_delivery_id', '=', False)], context=None)
            message_dict = {}
            for order in sale_obj.browse(cr, uid, oder_ids, context=None):
                picking_ids = self.search(cr, uid, [('so_id', '=', order.id),
                                                    ('state', 'not in', ('done', 'cancel')),
                                                    ('picking_type_code', '=', 'outgoing')], context=None)
                is_stock_up = all([True for picking_row in self.browse(cr, uid, picking_ids, context=None)
                                   if picking_row.state == 'assigned'] or [False])
                message = "%s%s(%s)" % (order.partner_id.name, order.name,
                                         '√' if is_stock_up else 'x')
                message_dict.setdefault(order.user_id.id, ['%s销售订单备货情况'% tomrrow_date]).append(message)
            for user_id, message_list in message_dict.iteritems():
                if receive_uid != SUPERUSER_ID: # 设置参数为1是 不给销售人员发消息，避免测试数据库重复发送
                    sale_obj.send_message(cr, uid, user_id, '\n'.join(message_list), context=None)
                if receive_uid:
                    sale_obj.send_message(cr, uid, receive_uid, '\n'.join(message_list), context=None)
        return True

    def unlink(self, cr, uid, ids, context=None):
        # 因为出库单是和销售订单 和采购单进行关联的
        # 采购单、销售订单上都有已建 出入库单的字段 这个字段是根据有效的单据进行填写的】
        # 如果删除或者取消就要修改已有单据上的数据进行同步
        for stock_pickings in self.pool.get("stock.picking").browse(cr, uid, ids, context=context):
            if stock_pickings.state != 'cancel':
                stock_pickings.action_cancel()
            # sale_order_obj = self.pool.get("sale.order")
            # sale_order_line_obj = self.pool.get("sale.order.line")
            # purchase_order_obj = self.pool.get("purchase.order")
            # purchase_order_line_obj = self.pool.get("purchase.order.line")
            # for stock_picking_row in stock_pickings:
            #     if stock_picking_row.so_id and stock_picking_row.state != 'cancel':
            #         #if (stock_picking_row.origin)[0] == 'S':
            #         sale_order_id = stock_picking_row.so_id.id
            #         # 退货的picking
            #         if stock_picking_row.origin_picking_id:  # (stock_picking_row.origin)[0] == 'W'
            #             # 销售订单生成出库单的  退货单 取消时的操作
            #             for stock_move_line in stock_picking_row.move_lines:
            #                 for sale_order_line_id in sale_order_line_obj.search(cr, uid, [(
            #                         "order_id", "=", stock_picking_row.so_id.id), ("product_id", "=", stock_move_line.product_id.id)], context=context):
            #                     sale_order_lines = sale_order_line_obj.browse(
            #                         cr, uid, sale_order_line_id, context=context)
            #                     if sale_order_lines.product_uom_qty > 0 and sale_order_lines.delivered_qty + stock_move_line.product_uom_qty > sale_order_lines.product_uom_qty:
            #                         raise osv.except_osv(
            #                             u'错误', u"不能删除，因为已发货数量超出了订单行数量")
            #                     sale_order_lines.write({"delivered_qty": sale_order_lines.delivered_qty + stock_move_line.product_uom_qty})
            #         else:
            #             # 出库的picking
            #             if stock_picking_row.picking_type_code.find('outgoing') >= 0:
            #                 is_shipment_completion = True
            #                 for stock_move_line in stock_picking_row.move_lines:
            #                     if is_shipment_completion and stock_move_line.product_uom_qty > 0:
            #                         is_shipment_completion = False
            #                     sale_order_line_id = sale_order_line_obj.search(cr, uid, [(
            #                         "order_id", "=", sale_order_id), ("product_id", "=", stock_move_line.product_id.id)],
            #                                                                     context=context)
            #                     sale_order_lines = sale_order_line_obj.browse(
            #                         cr, uid, sale_order_line_id, context=context)
            #                     sale_order_lines.write(
            #                         {"delivered_qty": sale_order_lines.delivered_qty - stock_move_line.product_uom_qty})
            #                 if not is_shipment_completion:
            #                     sale_order_obj.write(cr, uid, sale_order_id, {
            #                         "shipment_completion": False}, context=context)
            #             # 还有一种情况：样品退还
            #             if stock_picking_row.picking_type_code.find('incoming') >= 0:
            #                 pass
            #
            #     #采购入（出）库单的处理
            #     if stock_picking_row.origin and stock_picking_row.state != 'cancel':
            #         # 当关联的入库单删除时,相应减少采购单行上的产品已入库数
            #         if (stock_picking_row.origin)[0] == 'P':
            #             purchase_order_id = purchase_order_obj.search(
            #                 cr, uid, [("name", "=", stock_picking_row.origin)], context=context)
            #             is_shipped = True
            #             for stock_move_line in stock_picking_row.move_lines:
            #                 if is_shipped and stock_move_line.product_uom_qty > 0:
            #                     is_shipped = False
            #                 purchase_order_line_id = purchase_order_line_obj.search(cr, uid, [(
            #                     "order_id", "=", purchase_order_id), ("product_id", "=", stock_move_line.product_id.id)], context=context)
            #                 purchase_order_lines = purchase_order_line_obj.browse(
            #                     cr, uid, purchase_order_line_id, context=context)
            #                 purchase_order_lines.write({
            #                     "delivered_qty": purchase_order_lines.delivered_qty - stock_move_line.product_uom_qty
            #                 })
            #
            #             if not is_shipped:
            #                 purchase_order_obj.write(cr, uid, purchase_order_id, {
            #                                          "pick_state": False}, context=context)
            #         elif stock_picking_row.origin_picking_id.origin[0] == 'P':
            #             # 销售订单生成出库单的  退货单 取消时的操作
            #             purchase_line_obj = self.pool.get('purchase.order.line')
            #             for stock_move_line in stock_picking_row.move_lines:
            #                 line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=',
            #                                                                stock_picking_row.origin_picking_id.po_id.id),
            #                                                               ('product_id', '=', stock_move_line.product_id.id)
            #                                                               ], context=context)
            #                 for purcahse_line_row in purchase_line_obj.browse(cr, uid, line_ids, context=context):
            #                      if purcahse_line_row.delivered_qty + stock_move_line.product_uom_qty > \
            #                             purcahse_line_row.product_qty:
            #                         raise osv.except_osv(u'错误', u"不能删除，因为已建发货单数量数量超出了订单行数量")
            #                      purcahse_line_row.write({"delivered_qty": purcahse_line_row.delivered_qty +
            #                                                                   stock_move_line.product_uom_qty})
        return super(stock_picking, self).unlink(cr, uid, ids, context=context)

    def create(self, cr, uid, vals, context=None):
        # 对销售订单或者时采购单新建的 出入库单进行强关联。
        # 并且对于销售订单采购单直接生成的 出入库单的  退货单的数据也进行强关联
        if vals.get('origin'):
            if vals.get('origin')[0] == 'S':  # 如果是销售订单
                order_id = self.pool.get('sale.order').search(
                    cr, uid, [('name', '=', vals.get('origin'))], context=context)[0]
                vals.update({'so_id': order_id})
            elif vals.get('origin')[0] == 'P':  # 如果是采购单
                order_id = self.pool.get('purchase.order').search(
                    cr, uid, [('name', '=', vals.get('origin'))], context=context)[0]
                vals.update({'po_id': order_id})
            else:  # 如果是有出库或者入库单生成的退货单时
                warehouse_ids = self.search(
                    cr, uid, [('name', '=', vals.get('origin'))], context=context)
                if warehouse_ids:
                    warehouse_objs = self.browse(
                        cr, uid, warehouse_ids[0], context=context)
                    if warehouse_objs.so_id:
                        vals.update({'so_id': warehouse_objs.so_id.id})
                    elif warehouse_objs.po_id:
                        vals.update({'po_id': warehouse_objs.po_id.id})
        vals.update({'copy_state': 'draft'})
        return super(stock_picking, self).create(cr, uid, vals, context=context)

    def out_gonig_notify_sale(self, cr, uid, stock_picking_row, context=None):
        ##IEF5T 微信扫码出库单确认领货后（出库单写入领货人后）发消息给销售订单对应的销售员和助理
        picking_ids = self.search(cr, SUPERUSER_ID, [('state', 'not in', ('done', 'cancel')),
                                                     ('so_id', '=', stock_picking_row.so_id.id),
                                                     ('picking_type_code', '=', 'outgoing')
                                                     ], context=context)
        if len(picking_ids) == 0:
            delivery_str = u'全部'
        else:
            delivery_str = u'部分'
        if delivery_str:
            send_message = u'%s%s,已经%s领货准备送货了，领货人：%s\n' % ( stock_picking_row.so_id and
                                                                    stock_picking_row.so_id.partner_id.name,
                                                                    stock_picking_row.so_id and
                                                                    stock_picking_row.so_id.name,
                                                                    delivery_str,
                                                                    stock_picking_row.delivery_user_id.name)
            if stock_picking_row.so_id.user_id:
                self.pool.get('sale.order').send_message(cr, SUPERUSER_ID, stock_picking_row.so_id.user_id.id,
                                                         send_message, context=context)
            if stock_picking_row.so_id.share_to_uid:
                self.pool.get('sale.order').send_message(cr, SUPERUSER_ID, stock_picking_row.so_id.share_to_uid.id,
                                                         send_message, context=context)
        return True

    def write(self, cr, uid, ids, vals, context=None):
        # 继承发货的write方法捕捉发货完成 的状态  然后进行判断来实现目的
        # 1.出库单发货完成 要修改销售订单的（增加）  已发货数量  写入销售订单中此产品的成本价（用于计算销售订单的毛利）
        # 2.出货单的退货单 完成收货 要修改（减少） 销售订单的 已发货数量
        # 3.采购订单生成的出库单 退货单移动时 要减少采购单订单上已入库数量
        retrun_stock_picking = super(stock_picking, self).write(
            cr, uid, ids, vals, context=context)
        for stock_picking_row in self.browse(cr, uid, ids, context=context):
            if (stock_picking_row.origin_picking_id and stock_picking_row.origin_picking_id.so_id or stock_picking_row.so_id)\
                    and stock_picking_row.state == 'done' and vals.get('date_done', False):
                product_dict_list = []
                stock_move = self.pool.get('stock.move').search(
                    cr, uid, [('picking_id', '=', stock_picking_row.id)], context=context)
                for move_line in self.pool.get('stock.move').browse(cr, uid, stock_move, context=context):
                    price = 0
                    num = 0
                    for quant_id in move_line.quant_ids:
                        price = price + (quant_id.cost * quant_id.qty)
                        num = num + quant_id.qty
                    product_dict_list.append(
                        {'product_id': move_line.product_id.id, 'qty': move_line.product_uom_qty,
                         'price': num and round(price / num, 2) or 0})
                for prduct_dict in product_dict_list:
                    order_line_ids = 0
                    operation = ""
                    if stock_picking_row.picking_type_code.find('incoming')>=0:
                        if stock_picking_row.origin_picking_id:
                            operation = '>'
                        else:
                            operation = '<'
                    elif stock_picking_row.picking_type_code.find('outgoing')>=0:
                        if stock_picking_row.origin_picking_id:
                            operation = '<'
                        else:
                            operation = '>'
                    order_line_ids = self.pool.get('sale.order.line').search(
                        cr, uid, [('order_id', '=', stock_picking_row.so_id.id), ('product_id', '=', prduct_dict.get('product_id')), ('product_uom_qty', operation, 0)])
                    if order_line_ids and order_line_ids != 0:
                        #if stock_picking_obj.origin[0] == 'S':
                        if stock_picking_row.so_id:
                            #如果有origin_picking_id说明是点击"退货"按钮生成的退货单
                            if stock_picking_row.origin_picking_id:
                                # 销售订单生成的出库单 退货单移动时 要减少销售订单上已入库数量
                                order_line_obj = self.pool.get('sale.order.line').browse(
                                    cr, uid, order_line_ids, context=context)[0]
                                order_line_obj.write(
                                    {'shipped_qty': order_line_obj.shipped_qty - prduct_dict.get('qty')})
                            #正常销售出库单
                            else:
                                # 销售订单生成的 出库单进行移动后要把，成本价更新，然后要更新已移动的数量
                                order_line_obj = self.pool.get('sale.order.line').browse(
                                    cr, uid, order_line_ids, context=context)[0]
                                if order_line_obj.product_uom_qty > 0:
                                    # 出库单退货没有完成 就再次移动退货就会报这个错误
                                    if order_line_obj.shipped_qty + prduct_dict.get('qty') > order_line_obj.product_uom_qty:
                                        raise osv.except_osv(
                                            u'错误', u"不能进行移动操作，因为本次操作会导致出库数量大于订单的数量!请核实退货单已入库")
                                order_line_val = {'shipped_qty': order_line_obj.shipped_qty + prduct_dict.get('qty')}
                                cost_price_theoretical = round(
                                    ((prduct_dict.get('price') * prduct_dict.get('qty') + order_line_obj.shipped_qty *
                                      order_line_obj.cost_price) / (order_line_obj.shipped_qty + prduct_dict.get('qty'))), 2)
                                order_line_val.update({'cost_price': cost_price_theoretical})
                                order_line_obj.write(order_line_val)
            if vals.get('delivery_user_id', False) and stock_picking_row.so_id:
                self.out_gonig_notify_sale(cr, uid, stock_picking_row, context=context)
            if stock_picking_row.origin_picking_id and stock_picking_row.origin_picking_id.po_id \
                and stock_picking_row.state == 'done' \
                and vals.get('date_done', False):
                order_line_ids = [move_line.purchase_line_id.id for move_line in stock_picking_row.move_lines
                                  if move_line.state not in ('done', 'cancel', 'waiting')]
                # po_id=stock_picking_obj.po_id.id or stock_picking_obj.origin_picking_id and stock_picking_obj.origin_picking_id.po_id.id
                # order_line_ids = self.pool.get("purchase.order.line").search(cr, uid, [(
                #     "order_id", "=", po_id)], context=context)
                stock_move = self.pool.get('stock.move').search(
                    cr, uid, [('picking_id', '=', stock_picking_row.id)], context=context)
                prduct_dict = {}
                for move_line in self.pool.get('stock.move').browse(cr, uid, stock_move, context=context):
                    prduct_dict.update(
                        {(move_line.product_id.id,move_line.prod_spec): move_line.product_uom_qty})
                if stock_picking_row.origin and stock_picking_row.origin[0] == 'P':
                    for order_line in self.pool.get('purchase.order.line').browse(cr, uid, order_line_ids, context=context):
                        if order_line.incomed_qty>=abs(order_line.product_qty) or abs(prduct_dict.get((order_line.product_id.id,order_line.prod_spec), 0)) +abs(order_line.incomed_qty) > abs(order_line.product_qty):
                            raise osv.except_osv(
                                u'错误', u"不能进行移动操作，因为本次操作会导致出库数量大于订单的数量!")
                        order_line.write(
                            {'incomed_qty': prduct_dict.get((order_line.product_id.id,order_line.prod_spec), 0) + order_line.incomed_qty})
                elif stock_picking_row.origin:
                    # 采购订单生成的出库单 退货单移动时 要减少采购单订单上已入库数量
                    origin_order_line_ids = [move_line.purchase_line_id.id for move_line in stock_picking_row.origin_picking_id.move_lines]
                    for order_line_obj in self.pool.get('purchase.order.line').browse(
                            cr, uid, origin_order_line_ids, context=context):
                        if order_line_obj.incomed_qty - prduct_dict.get((order_line_obj.product_id.id,order_line_obj.prod_spec), 0) >= 0:
                            order_line_obj.write(
                                {'incomed_qty': order_line_obj.incomed_qty - prduct_dict.get((order_line_obj.product_id.id,order_line_obj.prod_spec), 0)})
                        else:
                            raise osv.except_osv(
                                    u'错误', u"不能进行移动操作，因为本次操作会导致出库数量大于订单的数量!")
        return retrun_stock_picking

    #重载系统的检查picking确认函数，修正采购退货单默认为强制检查可用问题(confirmed状态的picking记录不会执行到这里，所以可以注释)
    # def action_confirm(self, cr, uid, ids, context=None):
    #     todo = []
    #     todo_force_assign = []
    #     for picking in self.browse(cr, uid, ids, context=context):
    #         #不是出库单
    #         if picking.location_id.usage in ('supplier', 'inventory', 'production') and picking.picking_type_code.find('outgoing') < 0:
    #             todo_force_assign.append(picking.id)
    #         for r in picking.move_lines:
    #             if r.state == 'draft':
    #                 todo.append(r.id)
    #     if len(todo):
    #         self.pool.get('stock.move').action_confirm(cr, uid, todo, context=context)
    #
    #     if todo_force_assign:
    #         self.force_assign(cr, uid, todo_force_assign, context=context)
    #     return True

    def stock_picking_sign(self, cr, uid, ids, context=None):
        picking_row = self.browse(cr, uid, ids, context=context)
        values = {'delivery_done_time': fields.datetime.now()}
        if not picking_row.delivery_user_id:
            values.setdefault('delivery_user_id', uid)
        result = self.write(cr, uid, ids, values, context=context)
        order_row = picking_row.so_id
        # 判断条件， 是出库单 由销售订单生成 且 对应销售订单 填写了销售员
        if picking_row.picking_type_code == 'outgoing' and order_row and order_row.user_id:
            pick_ids = self.search(cr, uid, [('picking_type_code', '=', 'outgoing'),
                                                          ('so_id', '=', order_row.id)], context=context)
            for picking in self.browse(cr, uid, pick_ids, context=context):
                if not picking.delivery_done_time:
                    text_message = '%s的%s订单已部分送达，送货人：%s' \
                                   % (order_row.partner_id.name,
                                      order_row.name,
                                      picking_row.delivery_user_id.name)
                    break
            else:
                text_message = '%s的%s订单已全部送达，送货人：%s' \
                               % (order_row.partner_id.name,
                                  order_row.name,
                                  picking_row.delivery_user_id.name)

                self.pool.get('sale.order').send_message(cr, uid,
                                                order_row.user_id.id,
                                                text_message, context=context)
        return result

    def return_picking_is_full_returned(self, cr, uid, picking, context=None):

        for move in picking.move_lines:
            if move.product_uom_qty != sum([move.product_uom_qty for move in move.returned_move_ids]):
                return False
        return True

    def _compute_is_full_returned(self, cr, uid, ids, name, args, context=None):
        res = {}
        for picking in self.browse(cr, uid, ids, context=context):
            res[picking.id] = self.return_picking_is_full_returned(cr, uid, picking, context=context)
        return res

    def _get_new_return_move(self, cr, uid, ids, context=None):
        picking_ids = []
        for move_row in self.browse(cr, uid, ids, context=context):
            picking_ids.append(move_row.origin_returned_move_id.picking_id.id)
        return picking_ids

    def _get_so_po_expected_delivery_time(self, cr, uid, ids, name, args, context=None):
        res = {}
        for picking in self.browse(cr, uid, ids, context=context):
            res[picking.id] = picking.so_id.expected_delivery_time if picking.so_id else \
                picking.po_id and picking.po_id.planned_delivery_date or False
        return res

    def _get_sale_change_expected_delivery_time(self, cr, uid, ids, context=None):
        ids = ids if isinstance(ids, list) else [ids]
        picking_ids = self.pool.get('stock.picking').search(cr, uid, [('so_id', 'in', ids)], context=context)
        return picking_ids

    def _get_purchase_change_planned_delivery_date(self, cr, uid, ids, context=None):
        ids = ids if isinstance(ids, list) else [ids]
        picking_ids = self.pool.get('stock.picking').search(cr, uid, [('po_id', 'in', ids)], context=context)
        return picking_ids

    def get_image(self, cr, uid, path,context=None):
        open_file = open(os.path.join(tools.config.filestore(cr.dbname), path), 'rb')
        image_filed = open_file.read()
        open_file.close()
        return image_filed

    def _get_image_sign(self, cr, uid, ids, name, args, context=None):
        res = {}
        for picking_row in self.browse(cr, uid, ids, context=context):
            res[picking_row.id] = picking_row.image_sign_path and self.get_image(cr, uid, picking_row.image_sign_path, context=context)
        return res

    def _get_tracking_number_html(self, cr, uid, ids, name, args, context=None):
        res = {}

        return res

    _columns = {
        'source_location_id': fields.many2one('stock.location', string='源库位'),
        'dest_location_id': fields.many2one('stock.location', string='目的库位'),
        'so_id': fields.many2one('sale.order', string='关联的销售订单', copy=False, select=True),
        'po_id': fields.many2one('purchase.order', string='关联的采购单', copy=False, select=True),
        'invoice_id': fields.many2one('account.invoice', string='关联的发票', copy=False),
        'print_state': fields.boolean(u'是否已打印', track_visibility='onchange', copy=False),
        'origin_picking_id': fields.many2one('stock.picking', u'源单据', copy=False),
        'express_type_id':fields.many2one('express.type',u'快递方式', copy=False),
        # 'express_type_code': fields.char(u'快递单号', copy=False),
        'shipping_type_sale': fields.selection(
            [(u'express_delivery', u'快递'), (u'sale_delivery', u'销售送货'), (u'self_logistics', u'公司物流'),
             ('direct_delivery', u'供应商直送'),(u'take_car', u'打车')], string=u"送货方式"),
        'expected_delivery_time': fields.function(_get_so_po_expected_delivery_time, type='date', readonly=True,
                                                 track_visibility='onchange', string='期望发货日期', store={
                'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['so_id', 'po_id'], 10),
                'sale.order': (_get_sale_change_expected_delivery_time, ['expected_delivery_time'], 10),
                'purchase.order': (_get_purchase_change_planned_delivery_date, ['planned_delivery_date'], 10)
            }),

        'expected_delivery_hour': fields.related('so_id', 'expected_delivery_hour', type='char', readonly=True,
                                                 string='期望具体时间',track_visibility='onchange'),
        'planned_delivery_date': fields.related('po_id', 'planned_delivery_date', type='date', readonly=True,
                                                 track_visibility='onchange', store=True, string='期望发货日期'),
        'picture_list_ids': fields.one2many('picture.list', 'picking_id', string='图片列表'),
        'copy_state': fields.selection([
                ('draft', '草稿'),
                ('cancel', '取消'),
                ('waiting', '等待其他操作'),
                ('confirmed', '待出库方确认'),
                ('partially_available', '部分可用'),
                ('assigned', '准备移动'),
                ('done', '完成'),
                ], track_visibility='onchange', string='状态追踪'),
        'is_full_returned': fields.function(_compute_is_full_returned, type='boolean', string='是否退货完成', select=True,
                                            store={
                                                'stock.move': (_get_new_return_move, ['origin_returned_move_id'], 10),
                                            }),
        'consignee': fields.char(u'收件人'),
        'street': fields.char(u'详细地址'),
        'zip': fields.char(u'邮编'),
        'mobile_number': fields.char(u'手机'),
        'phone_number': fields.char(u'电话'),
        'is_invisible_in_map': fields.boolean(u'在地图上隐藏'),
        'tracking_number': fields.text(u'快递单号'),
        'tracking_number_html': fields.function(_get_tracking_number_html, type='html', string=u'快递单号'),
        'packages_number': fields.integer(u'份数'),
        'total_weight_kilo': fields.float(u'总重量（公斤）'),
        'picking_user_id': fields.many2one('res.users', u'拣货人'),
        'picking_start_datetime': fields.datetime(u'拣货时间'),
        'is_direct_delivery': fields.boolean(u'是否直送'),
        'direct_delivery_po_id': fields.many2one('purchase.order', u'直送采购单')
    }

    def on_change_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
        if picking_type_id:
            picking_type_row = self.pool.get('stock.picking.type').browse(cr, uid, picking_type_id, context=context)
            if picking_type_row:
                return {'value': {'picking_type_code': picking_type_row[0].code}}


    #重载没有保留按钮,增加一个上下文标记,以便区别移动出库和没有保留
    @api.cr_uid_ids_context
    def do_unreserve(self, cr, uid, picking_ids, context=None):
        context.setdefault('is_do_unreserve', True)
        return super(stock_picking, self).do_unreserve(cr, uid, picking_ids, context=context)
    _defaults = {
        'delivery_state': 'waiting_delivery',
    }

class stock_move(osv.osv):
    _inherit = 'stock.move'

    # 重载根据picking建立发票方法，将invoice_id写入stock.picking
    def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
        invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
        # 如果出入库单有采购单关联，则写入采购单和发票关联表
        if move.picking_id.po_id:
            move.picking_id.po_id.write({'invoice_ids': [(4, invoice_line_vals['invoice_id'])]})
        # 出入库单写入发票id
        move.picking_id.write({'invoice_id': invoice_line_vals['invoice_id']})
        return invoice_line_id

    def create(self, cr, uid, vals, context=None):
        res_id = super(stock_move, self).create(cr, uid, vals, context=context)
        return res_id

    def write(self, cr, uid, ids, vals, context=None):
        if vals.get('state') != 'done':
            vals.update({'date': False})
        res_id = super(stock_move, self).write(cr, uid, ids, vals, context=context)
        return res_id

    # 根据销售订单行上的route_id进行push(根据stock.location.path)
    def _push_apply(self, cr, uid, moves, context=None):
        push_obj = self.pool.get("stock.location.path")
        so_line_obj = self.pool.get('sale.order.line')
        for move in moves:
            if not move.move_dest_id and not move.origin_returned_move_id:
                domain = [('location_from_id', '=', move.location_dest_id.id)]
                sale_order_row = move.picking_id.so_id
                if sale_order_row:
                    sale_order_line_ids = so_line_obj.search(cr, uid, [('order_id', '=', sale_order_row.id), ('product_id', '=', move.product_id.id)], context=context)
                    if sale_order_line_ids:
                        sale_order_line_row = so_line_obj.browse(cr, uid, sale_order_line_ids[0])
                        route_id = sale_order_line_row.route_id
                        if route_id:
                            rules = push_obj.search(cr, uid, domain + [('route_id', '=', route_id.id)], order='route_sequence, sequence', context=context)
                            if rules:
                                rule = push_obj.browse(cr, uid, rules[0], context=context)
                                push_obj._apply(cr, uid, rule, move, context=context)
        return True

    #判断采购单为某个销售订单 专门采购(判断是否入库就锁定库存)
    def purchase_server_one_sale(self, cr, uid, purchase_line_row, product_id, context=None):
        order_line_obj = self.pool.get('sale.order.line')
        purchase_row = purchase_line_row.order_id
        so_ids = eval(purchase_row.so_ids or '[]')
        so_code_list = purchase_row.so_code_list or '、'
        if len(so_ids) >= 1:
            order_line_ids = False
            sale_order_ids = self.pool.get('sale.order').search(cr, uid, [('id', 'in', so_ids),
                                                         ('state', 'not in', ('cancel', 'draft'))], context=context)
            if sale_order_ids:
                cr.execute("""SELECT sol.id FROM sale_order_line sol
                                LEFT JOIN sale_order so ON sol.order_id=so.id
                                WHERE so.id in %s AND product_id=%s AND
                                (shipped_qty=0 OR shipped_qty IS NULL) ORDER BY so.expected_delivery_time, date_order"""%(str(sale_order_ids).replace('[', '(').replace(']', ')'), product_id))
                order_line_ids = [ret[0] for ret in cr.fetchall()]
            if order_line_ids:
                return so_ids, order_line_ids
        return False, False

    def auto_lock_quant_by_sale(self, cr, uid, purchase_line_id, so_id, so_line_id, context=None):
        line_obj = self.pool.get('sale.order.line')
        location_ids = self.pool.get('sale.order').get_sale_order_location_id(cr,
                                                          uid, so_id and so_id[0], context=context)[1]
        move_ids = self.search(cr, uid, [('purchase_line_id', '=', purchase_line_id.id)], context=context)
        quant_obj = self.pool.get('stock.quant')
        quant_ids = []
        return_val = 0
        for move_row in self.browse(cr, uid, move_ids, context=context):
            quant_ids += quant_obj.search(cr, SUPERUSER_ID, [('history_ids', 'in', move_row.id),
                                                             ('location_id', 'in', location_ids),
                                                             ('reservation_id', '=', False),
                                                             ], context=context)
        # if sum([quant_row_sum.qty for quant_row_sum in quant_obj.browse(cr, uid, quant_ids, context=context)]) == \
        #         sum([sol_row.product_uom_qty for sol_row in line_obj.browse(cr, uid, so_line_id, context=context)]):
        for sol_row in self.pool.get('sale.order.line').browse(cr, uid, so_line_id, context=context):
            if sol_row.product_uom_qty <= 0:
                continue
            currency_qty = sol_row.product_uom_qty - sol_row.locked_quant_qty - sol_row.stock_out_locked_quant
            if currency_qty < 0:
                continue
            quant_currency_ids = quant_obj.search(cr, SUPERUSER_ID, [('history_ids', 'in', move_ids),
                                                             ('location_id', 'in', location_ids),
                                                             ('reservation_id', '=', False),
                                                             ], context=context)
            for quant_row in quant_obj.browse(cr, SUPERUSER_ID, quant_currency_ids, context=context):
                if currency_qty == 0:
                    break
                if currency_qty >= quant_row.qty:
                    return_val += 1
                    quant_row.write({'reservation_id': -1, 'so_id': sol_row.order_id.id, 'so_line_id': sol_row.id})
                    currency_qty = currency_qty - quant_row.qty
                else:
                    return_val += 1
                    quant_split_row = quant_obj._quant_split(cr, SUPERUSER_ID, quant_row,
                                                             quant_row.qty - currency_qty, context=context)
                    quant_split_row.write({'reservation_id': -1, 'so_id': sol_row.order_id.id,
                                           'so_line_id': sol_row.id})
                    break
        return return_val


    # 重写stock_move的action_done方法，写入入库完成时间
    def action_done(self, cr, uid, ids, context=None):
        #检查采购单状态，未检查可用的不能移动
        for move in self.browse(cr, uid, ids, context=context):
            # 这个代码写的时间太过久远，记不起来什么作用了，偶尔还会报出莫名其妙的错误， 暂时删除。
            # if move.picking_id and move.picking_id.picking_type_code.find('incoming') >= 0 and move.picking_id.state in ('draft', 'confirmed'):
            #     raise osv.except_osv(u'错误', move.picking_id.origin + u"采购单未检查可用")
            #为了虚拟仓库出库流程，允许取消的销售订单可以移动出库
            if move.so_line_id and move.so_change_id and move.so_line_id.order_id.state in ['cancel', 'draft']:
                raise osv.except_osv(u'错误', u"销售订单%s还在修改中，请和销售联系再进行操作！"%move.so_line_id.order_id.name)
        res = super(stock_move, self).action_done(
            cr, uid, ids, context=context)
        picking_obj = self.pool.get('stock.picking')
        picking_ids = set()
        # 找到当前move对应的picking
        for move in self.browse(cr, uid, ids, context=context):
            picking_ids.add(move.picking_id.id)
            sum_move_in_qty = move.product_uom_qty + move.purchase_line_id.incomed_qty
            #有对应的采购订单行才做已入库数的计算
            if move.purchase_line_id:
                #采购数量
                product_qty = move.purchase_line_id.product_qty
                if sum_move_in_qty > abs(product_qty):
                    error_message = u'产品"%s"的总入库数量%s超过了采购数量%s' % (move.product_id.name, sum_move_in_qty, product_qty)
                    if product_qty < 0:
                        error_message = u'产品"%s"的总出库退货数量%s超过了采购数量%s' % (move.product_id.name, sum_move_in_qty, product_qty)
                    raise osv.except_osv(u'错误', error_message)
                move.purchase_line_id.write({'incomed_qty': sum_move_in_qty})
                #判断采购单为销售订单专门采购 如果是 保留 锁定库存
                if move.picking_id and move.picking_id.picking_type_code == 'incoming':
                    so_id, so_line_id = self.purchase_server_one_sale(cr, uid, move.purchase_line_id,
                                                                  move.product_id.id, context=context)
                    if so_id:
                        self.auto_lock_quant_by_sale(cr, uid, move.purchase_line_id, so_id, so_line_id, context=context)
                self.set_quant_cost(cr, uid, [move.id], context=context)
        done_picking = []
        for picking in picking_obj.browse(cr, uid, list(picking_ids), context=context):
            if picking.state == 'done' and not picking.date_done:
                done_picking.append(picking.id)
        # 对完成状态但完成日期为空的picking写入系统默认日期
        if done_picking:
            picking_obj.write(cr, uid, done_picking, {'date_done': time.strftime(
                DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
        return res

    def set_quant_cost(self, cr, uid, move_ids, context=None):
        """ quant产品一经入库就写入采购的价格 当作是成本价
        :return:
        """
        cr.execute("""
            SELECT sqmr.quant_id,
                   pol.price_unit
            FROM purchase_order_line pol
              INNER JOIN stock_move sm ON sm.purchase_line_id = pol.id
              INNER JOIN stock_quant_move_rel sqmr ON sqmr.move_id = sm.id
              INNER JOIN stock_quant sq ON sq.id = sqmr.quant_id
            WHERE sm.id in %s;
        """, (tuple(move_ids),))
        res = cr.fetchall()
        for quant_id, price_unit in res:
            self.pool.get('stock.quant').write(cr, SUPERUSER_ID, quant_id, {'cost': price_unit}, context=context)
        return True

    def _get_expected_delivery_time(self, cr, uid, ids, fields, args, context=None):
        res = {}
        purchase_line_obj = self.pool.get('purchase.order.line')
        sale_order_change_obj = self.pool.get('sale.order.line.change')

        for move in self.browse(cr, uid, ids, context=context):
            purchase_line_ids = purchase_line_obj.search(
                cr, uid, [('id', 'in', [move.purchase_line_id.id])], context=context)
            delivery_time_list = []
            for purchase_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context):
                # 根据采购单id, 找到对应的销售订单变更表ids
                sale_order_change_ids = sale_order_change_obj.search(
                    cr, uid, [('po_id', 'in', [purchase_line.order_id.id])], context=context)
                # 获得所有销售订单变更表对应的期望发货日期，并从小到大排序
                if sale_order_change_ids:
                    before_so_id = 0
                    for change_order in sale_order_change_obj.browse(cr, uid, sale_order_change_ids, context=context):
                        if change_order.expected_delivery_datetime:
                            # if排除一个销售订单有多个订单行，一行会出现多个时间的问题
                            if before_so_id != change_order.so_id.id:
                                # 根据时区转换时间，如果仍以时间显示不需要转换。但是转化成了string，需要手动转换
                                date_time = datetime.strptime(
                                    str(change_order.expected_delivery_datetime), '%Y-%m-%d %H:%M:%S')
                                date_time = date_time.replace(tzinfo=pytz.utc).astimezone(
                                    pytz.timezone('Asia/Shanghai'))
                                date_time = date_time.strftime('%Y-%m-%d')
                                delivery_time_list.append(date_time)

                        before_so_id = change_order.so_id.id
                    delivery_time_list.sort()
                res[move.id] = ",".join(delivery_time_list)
        return res

    def _get_sale_order_delivery_time(self, cr, uid, ids, context=None):
        # 通过销售订单找到订单变更表，再找到采购单，再找到采购订单行，进而找到move id
        result = {}
        move_ids = set()
        line_change_obj = self.pool.get('sale.order.line.change')
        purchase_order_obj = self.pool.get('purchase.order')
        line_change_ids = line_change_obj.search(
            cr, uid, [('so_id', 'in', ids)], context=context)
        for change_line in line_change_obj.browse(cr, uid, line_change_ids, context=context):
            if not change_line.po_id:
                continue
            for purchase in purchase_order_obj.browse(cr, uid, [change_line.po_id.id], context=context):
                for order_line in purchase.order_line:
                    if not order_line.move_ids:
                        continue
                    for move_id in order_line.move_ids:
                        move_ids.add(move_id)
            for move in move_ids:
                result[move.id] = True
        return result.keys()
    def choose_location_id_wizard(self,cr,uid,ids,context):
        model_data_obj = self.pool.get('ir.model.data')
        choose_location_wizard_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_choose_location_wizard_form')
        stock_move_rows = self.browse(cr, uid, ids, context=context)
        # 目的库位不等于view时自动入库
        if '食品' not in stock_move_rows.product_id.categ_id.display_name:
            if stock_move_rows and stock_move_rows[0].location_dest_id.usage != 'view':
                self.action_done(cr, uid, ids, context=context)
                return True
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'choose.location.wizard',
            'view_mode': 'form',
            'view_type': 'form',
            'name': u'选择库位',
            'views': [(choose_location_wizard_id, 'form')],
            'context':"{'default_move_id':"+str(ids[0])+"}",
            'target': 'new',
        }

    # 检查可用时如果全部锁定,则变更记录改为已锁定
    def action_assign(self, cr, uid, ids, context=None):
        """ Checks the product type and accordingly writes the state.
        """
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        context = context or {}
        quant_obj = self.pool.get("stock.quant")
        delivery_obj = self.pool.get('apply.delivery')
        to_assign_moves = set()
        main_domain = {}
        todo_moves = []
        operations = set()
        for move in self.browse(cr, uid, ids, context=context):
            if move.state not in ('confirmed', 'waiting', 'assigned'):
                continue
            if move.location_id.usage in ('supplier', 'inventory', 'production'):
                to_assign_moves.add(move.id)
                # in case the move is returned, we want to try to find quants before forcing the assignment
                if not move.origin_returned_move_id:
                    continue
            if move.product_id.type == 'consu':
                to_assign_moves.add(move.id)
                continue
            else:
                todo_moves.append(move)
                # we always keep the quants already assigned and try to find the remaining quantity on quants not assigned only
                main_domain[move.id] = [('reservation_id', '=', False), ('qty', '>', 0)]
                # main_domain的条件增加move源 库位的条件(因为检查可用时的domain缺少传入location_id导致将客户库位给锁了)
                main_domain[move.id] = [('reservation_id', '=', False), ('qty', '>', 0),
                                        ('location_id', 'child_of', move.location_id.id)]
                if move.picking_id.so_id:
                    so_id = move.picking_id.so_id.id
                    if move.picking_id.so_id.order_type_id.id == card_exchange_order_id:
                        delivery_dict = delivery_obj.search_read(cr, uid, [('so_id', '=', move.picking_id.so_id.id)],
                                                                 ['card_so_id'], context=context)
                        if delivery_dict and delivery_dict[0].get('card_so_id'):
                            so_id = delivery_dict[0].get('card_so_id')[0]
                    main_domain[move.id] = [('qty', '>', 0), ('location_id', 'child_of', move.location_id.id),
                                            '|', '&', ('reservation_id', '=', -1),
                                            ('so_id', '=', so_id),
                                            ('reservation_id', '=', False)]
                # if the move is preceeded, restrict the choice of quants in the ones moved previously in original move
                ancestors = self.find_move_ancestors(cr, uid, move, context=context)
                if move.state == 'waiting' and not ancestors:
                    # if the waiting move hasn't yet any ancestor (PO/MO not confirmed yet), don't find any quant available in stock
                    main_domain[move.id] += [('id', '=', False)]
                elif ancestors:
                    main_domain[move.id] += [('history_ids', 'in', ancestors)]

                # if the move is returned from another, restrict the choice of quants to the ones that follow the returned move
                if move.origin_returned_move_id and not move.purchase_line_id:
                    #  not move.purchase_line_id 条件 是为了避免采购退货不成功（因为，大部分情况下 采购是不可能满足 从供应商拿的货 退给供应商）
                    main_domain[move.id] += [('history_ids', 'in', move.origin_returned_move_id.id)]
                for link in move.linked_move_operation_ids:
                    operations.add(link.operation_id)
        # Check all ops and sort them: we want to process first the packages, then operations with lot then the rest
        operations = list(operations)
        # 增加库位排序
        operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0)
                                      + (x.location_id.id)
                                      + (x.lot_id and -1 or 0))
        for ops in operations:
            # first try to find quants based on specific domains given by linked operations
            for record in ops.linked_move_operation_ids:
                move = record.move_id
                if move.id in main_domain:
                    domain = main_domain[move.id] + self.pool.get('stock.move.operation.link').get_specific_domain(
                        cr, uid, record, context=context)
                    qty = record.qty
                    if qty:
                        quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id,
                                                                      qty, domain=domain, prefered_domain_list=[],
                                                                      restrict_lot_id=move.restrict_lot_id.id,
                                                                      restrict_partner_id=move.restrict_partner_id.id,
                                                                      context=context)
                        quant_obj.quants_reserve(cr, uid, quants, move, record, context=context)
        for move in todo_moves:
            if move.linked_move_operation_ids:
                continue
            # then if the move isn't totally assigned, try to find quants without any specific domain
            if move.state != 'assigned':
                qty_already_assigned = move.reserved_availability
                qty = move.product_qty - qty_already_assigned
                quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty,
                                                              domain=main_domain[move.id], prefered_domain_list=[],
                                                              restrict_lot_id=move.restrict_lot_id.id,
                                                              restrict_partner_id=move.restrict_partner_id.id,
                                                              context=context)
                quant_obj.quants_reserve(cr, uid, quants, move, context=context)

        # force assignation of consumable products and incoming from supplier/inventory/production
        if to_assign_moves:
            self.force_assign(cr, uid, list(to_assign_moves), context=context)
        model_data_obj = self.pool.get('ir.model.data')
        stock_location_card_exchange_location_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.stock_location_card_exchange')
        for move in self.browse(cr, uid, ids, context=context):
            if (move.location_id.id == stock_location_card_exchange_location_id or (move.picking_id.so_id and move.picking_id.so_id.name.startswith('JF_')))\
                    and move.reserved_availability == move.product_qty and move.so_change_id:
                move.so_change_id.write({'po_id': -1})
                # 主要針對卡券提货记录（逻辑上还是有缺陷不能适应大部分订单， move 上无法记录多个change_ids 和
                # 2. change_id 表上的order_line 字段可能确失导致不对， 已处理的销售订单行删除，并添加一个相同数量产品的行，# ）
        return True

    def _default_location_destination(self, cr, uid, context=None):
        context = context or {}
        if context.get('location_dest_id', False):
            return context.get('location_dest_id', False)
        if context.get('default_picking_type_id', False):
            pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'],
                                                                   context=context)
            return pick_type.default_location_dest_id and pick_type.default_location_dest_id.id or False
        return False

    def _default_location_source(self, cr, uid, context=None):
        context = context or {}
        if context.get('location_id', False):
            return context.get('location_id', False)
        if context.get('default_picking_type_id', False):
            pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'],
                                                                   context=context)
            return pick_type.default_location_src_id and pick_type.default_location_src_id.id or False
        return False

    def _get_sale_or_purchase_note(self, cr, uid, ids, name, args, context=None):
        res = {}
        for move in self.browse(cr, uid, ids, context=context):
            res[move.id] = move.purchase_line_id and move.purchase_line_id.purchase_spec or \
                           move.so_line_id and move.so_line_id.sale_note or ''
        return res

    def _get_sale_order_line_related_move(self, cr, uid, ids, context=None):
        move_list = []
        for line_id in self.browse(cr, uid, ids, context=context):
            move_list += self.pool.get("stock.move").search(cr, uid, [('so_line_id', '=', line_id.id)], context=context)
        return move_list

    def _get_purchase_order_line_related_move(self, cr, uid, ids, context=None):
        move_list = []
        for line_id in self.browse(cr, uid, ids, context=context):
            move_list += self.pool.get("stock.move").search(cr, uid, [('purchase_line_id', '=',
                                                                       line_id.id)], context=context)
        return move_list


    def onchange_quantity(self, cr, uid, ids, product_id, product_qty, product_uom, product_uos):
        """ On change of product quantity finds UoM and UoS quantities
        @param product_id: Product id
        @param product_qty: Changed Quantity of product
        @param product_uom: Unit of measure of product
        @param product_uos: Unit of sale of product
        @return: Dictionary of values
        """
        result = {
            'product_uos_qty': 0.00
        }
        warning = {}
        if (not product_id) or (product_qty <= 0.0):
            result['product_qty'] = 0.0
            return {'value': result}
        product_obj = self.pool.get('product.product')
        uos_coeff = product_obj.read(cr, uid, product_id, ['uos_coeff'])
        # Warn if the quantity was decreased
        if ids:
            for move in self.browse(cr, uid, ids, context=None):
                if product_uom != move.product_uom_qty and move.picking_id.picking_type_code != 'internal':
                    warning.update({
                        'title': _('Information'),
                        'message': _("By changing this quantity here, you accept the "
                                "new quantity as complete: Odoo will not "
                                "automatically generate a back order.")})
                    return {'value': { 'product_uom_qty':  move.product_uom_qty,
                                       'product_uos_qty': move.product_uom_qty }, 'warning': warning}
                break
        if product_uos and product_uom and (product_uom != product_uos):
            precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product UoS')
            result['product_uos_qty'] = float_round(product_qty * uos_coeff['uos_coeff'], precision_digits=precision)
        else:
            result['product_uos_qty'] = product_qty

            return {'value': result, 'warning': warning}

    def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False, loc_dest_id=False, partner_id=False):
        """ On change of product id, if finds UoM, UoS, quantity and UoS quantity.
        @param prod_id: Changed Product id
        @param loc_id: Source location id
        @param loc_dest_id: Destination location id
        @param partner_id: Address id of partner
        @return: Dictionary of values
        """
        if ids:
            for move in self.browse(cr, uid, ids, context=None):
                warning = {}
                if prod_id != move.product_id.id and move.picking_id.picking_type_code != 'internal':
                    warning.update({
                        'title': _('Information'),
                        'message': _("自动创建的出入库单不允许修改产品")})
                    return {'value': {'product_id': move.product_id.id}, 'warning': warning}

        if not prod_id:
            return {}
        user = self.pool.get('res.users').browse(cr, uid, uid)
        lang = user and user.lang or False
        if partner_id:
            addr_rec = self.pool.get('res.partner').browse(cr, uid, partner_id)
            if addr_rec:
                lang = addr_rec and addr_rec.lang or False
        ctx = {'lang': lang}

        product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0]
        uos_id = product.uos_id and product.uos_id.id or False
        result = {
            'name': product.partner_ref,
            'product_uom': product.uom_id.id,
            'product_uos': uos_id,
            'product_uom_qty': 1.00,
            'product_uos_qty':
                self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)[
                    'value']['product_uos_qty'],
        }
        if loc_id:
            result['location_id'] = loc_id
        if loc_dest_id:
            result['location_dest_id'] = loc_dest_id
        return {'value': result}


    _columns = {
        'prod_spec': fields.char(u'型号备注', copy=False),
        'expected_delivery_time': fields.function(_get_expected_delivery_time, type='char', string='预计发货日期', select=True,
                                                  store={
                                                      'stock.move': (lambda self, cr, uid, ids, c={}: ids, ['purchase_line_id'], 10),
                                                      'sale.order': (_get_sale_order_delivery_time, ['expected_delivery_time'], 16)
                                                  }),
        'is_direct_delivery': fields.boolean(u'是否直送', copy=False),
        'temp_incomed_qty': fields.float(u'临时入库数', digits_compute=dp.get_precision('Product UoS'), copy=False),
        'temp_delivered_qty': fields.float(u'临时出库数', digits_compute=dp.get_precision('Product UoS'), copy=False),
        'prod_model': fields.related('product_id', 'prod_model', type='char', string=u'产品型号', readonly="1", store=True),
        'planned_delivery_date': fields.related('picking_id', 'planned_delivery_date', type='date',
                                                string=u'期望发货日期', readonly="1"),
        'so_change_id': fields.many2one('sale.order.line.change', u'变更记录行', copy=False, select=True),
        #主要針對卡券提货记录（逻辑上还是有缺陷不能适应大部分订单， move 上无法记录多个change_ids 和
        #2. change_id 表上的order_line 字段可能确失导致不对， 已处理的销售订单行删除，并添加一个相同数量产品的行，# ）
        'so_line_id': fields.many2one('sale.order.line', u'销售行记录', copy=False, select=True),
        'note': fields.function(_get_sale_or_purchase_note, u'备注', type="text", store={
            'stock.move': (lambda self, cr, uid, ids, c={}: ids, ['purchase_line_id', 'so_line_id'], 10),
            'sale.order.line': (_get_sale_order_line_related_move, ['sale_note'], 10),
            'purchase.order.line': (_get_purchase_order_line_related_move, ['purchase_spec'], 10),
        }),
        'expiration_date': fields.date(u'过期时间'),
        'date': fields.datetime('Date', required=False, select=True,
                                help="Move date: scheduled date until move is done, then date of actual move processing",
                                states={'done': [('readonly', True)]}),
        'is_card': fields.related('picking_id', 'is_card', type='boolean', string='卡券订单', store=True, select=True),
        'po_id': fields.related('picking_id', 'po_id', type='many2one', relation='purchase.order', select=True,
                                string='卡券订单', store=True),

        'purchase_uid': fields.related('purchase_line_id', 'create_uid', type='many2one', string='采购员',
                                           store=True, relation='res.users'),
        'purchase_expected_delivery_time': fields.related('purchase_line_id', 'expected_delivery_time', type='date', string='采购员'),
    }

    def update_move_sale_cost(self, cr, uid, move_id, context=None):
        """根据move记录找到对应的销售订单行 然后找到 销售订单行对应的所有的
        move 记录和quant记录就算出成本价 回写销售订单
        """
        move_row = self.browse(cr, uid, move_id, context=context)
        if move_row.so_line_id:
            cr.execute("""
                       SELECT sum(sq.qty),
                              sum(sq.qty*sq.cost)
                           from stock_move sm
                           INNER JOIN stock_quant_move_rel sqmr ON sqmr.move_id = sm.id
                           INNER JOIN stock_quant sq ON sq.id = sqmr.quant_id
                       where sm.so_line_id=%s
                   """ % move_row.so_line_id.id)
            res = cr.fetchall()
            if res and res[0] and res[0][0]:
                write_vals = {'cost_price_test': res[0][1]/res[0][0]}
                write_vals.update({'cost_price': res[0][1]/res[0][0]})
                move_row.so_line_id.write(write_vals)
        return True

    _defaults = {
        'location_id': _default_location_source,
        'location_dest_id': _default_location_destination,
    }


class dftg_stock_transfer_details(osv.TransientModel):
    _inherit = 'stock.transfer_details'
    _description = 'Picking wizard'

    @api.one
    def do_detailed_transfer(self):
        # 继承系统的do_detailed_transfer()方法，当入库完成时，给采购单的pick_state和income_finished_time字段赋值
        for items in self.item_ids:
            for move_id in list(set([oprator.move_id.id for oprator in items.packop_id.linked_move_operation_ids])):
                self.pool['stock.move'].write(self._cr, self._uid, move_id,
                                              {'expiration_date': items.expiration_date}, context=self._context)
        return super(dftg_stock_transfer_details, self).do_detailed_transfer()

# 设计思路的变动, 现在不需要在 库位上增加订单的类型
class message_message(osv.osv_memory):
    _name = "message.message"
    _columns = {
        'message': fields.char(u'要发送的消息'),
    }

    def send_message(self, cr, uid, ids, context=None):
        message_objs = self.browse(cr, uid, ids, context=context)
        users = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        content = message_objs[0].message
        #发送到微信消息（群发给应用“一起工作”，用户组：员工组）
        # (return_content, error) = wx_api.send_text(content, 10, toparty=['2'])
        # if error:
        #     raise osv.except_osv(u'错误' + str(error.code), error.message)

        #发送到bus消息
        self.pool.get('bus.bus').sendone(cr, uid, 'dftg_message', {'title': '%s的消息通知    %s' %
                                                                   (users.partner_id.name, datetime.now(pytz.timezone(context.get('tz') or 'Asia/Shanghai')).strftime(
                                                                       DEFAULT_SERVER_DATETIME_FORMAT)),
                                                                   'content': content})

class recreate_quant(osv.osv_memory):
    _name = 'recreate.quant'

    _columns = {
        'super_date': fields.date(u'共用参数（时间）'),
        'super_start_date': fields.datetime(u'共用参数（开始时间）'),
        'super_end_date': fields.datetime(u'共用参数（结束时间）'),
        'super_user': fields.many2one('res.users', u'共用参数（时间）'),
        'file_path': fields.char(u'wiki目录')
    }

    def get_quant_ids(self, cr, uid, ids, move, child_location, context=None):
        orderby = 'in_date'
        return_judge = ">0"
        # if move.get('origin_returned_move_id'):
        #     return_judge = "=%s" % move.get('origin_returned_move_id')
        cr.execute("""
            select DISTINCT sq.id, sq.qty,sq.location_id,
                sq.product_id, sq.in_date
                from stock_quant sq
                LEFT JOIN stock_quant_move_rel qml ON  qml.quant_id=sq.id
                where sq.qty > 0 and  sq.product_id=%s and
                 sq.location_id in %s and
                 sq.reservation_id IS NULL AND qml.move_id%s ORDER BY sq.in_date, sq.id desc
        """%(move.get('product_id'), str(child_location).replace('[','(').replace(']', ')'), return_judge))
        all_quant_ids = cr.dictfetchall()
        product_uom_qty = move.get('product_uom_qty')
        if move.get('location_id') in (8, 5) and product_uom_qty:
            # 新建quant并且插入对应的关系
            cr.execute("""INSERT INTO stock_quant (qty, product_id, location_id, in_date, company_id)
                                         VALUES (%s, %s, %s, '%s', 1) RETURNING id
                           """ % (product_uom_qty,  move.get('product_id'),
                                  move.get("location_dest_id"),  str(move.get('date') or move.get('write_date'))))
            quant_id_dict = cr.dictfetchone()
            new_qunat_id = quant_id_dict.get('id')
            self.insertRel(cr, uid, new_qunat_id, [move.get('id')], context=context)
            #插入 关系
        else:
            for quant in all_quant_ids:
                if product_uom_qty == 0:
                    break
                if quant.get('qty') == product_uom_qty:
                    # 修改目的库位插入quant的关系
                    cr.execute("""
                                  update stock_quant set location_id=%s WHERE id=%s
                               """ % (move.get('location_dest_id'), quant.get('id')))
                    self.insertRel(cr, uid, quant.get('id'), [move.get('id')], context=context)
                    break
                elif float_compare(product_uom_qty - quant.get('qty'), 0, 2) > 0:
                    # 修改目的库位插入quant的关系
                    cr.execute("""
                                    update stock_quant set location_id=%s WHERE id=%s
                              """ % (move.get('location_dest_id'), quant.get('id')))
                    self.insertRel(cr, uid, quant.get('id'), [move.get('id')], context=context)
                    product_uom_qty = product_uom_qty - quant.get('qty')
                else:
                    # 新建quant 并复制全来的quant的全部的关系
                    quant_id = self.CreateQuant(cr, uid, quant.get('qty') - product_uom_qty,
                                     move.get('product_id'), quant.get('location_id'),
                                     quant.get('in_date'), context=context)
                    cr.execute("""select move_id from stock_quant_move_rel where quant_id=%s""" % quant.get('id'))
                    move_id_dict = cr.dictfetchall()
                    self.insertRel(cr, uid, quant_id, [id_dict.get('move_id') for
                                                       id_dict in move_id_dict], context=context)
                    #更新quant的目的库位更改quant的 数量 并且
                    cr.execute(""" update stock_quant set location_id=%s,qty=%s WHERE id=%s
                                             """ % (move.get('location_dest_id'), product_uom_qty, quant.get('id')))
                    self.insertRel(cr, uid, quant.get('id'), [move.get('id')], context=context)
                    break
            else:
                if product_uom_qty != 0:
                    move.update({'product_uom_qty': product_uom_qty})
                    return move
                    print 'product_id:', move.get('product_id'), 'product_uom_qty', product_uom_qty, 'move', 'picking_id:', move.get('picking_id')
                    #raise osv.except_osv(u'错误！',u'库位没有足够库存！%s'%(child_location))
        return False

    def CreateQuant(self, cr, uid, qty, product_id, location_id, indate, context=None):
        cr.execute("""
                        INSERT INTO stock_quant (qty, product_id, location_id, in_date, company_id)
                                    VALUES (%s, %s, %s, '%s', 1) RETURNING id
                      """ % (qty, product_id, location_id, str(indate)))
        id_dict = cr.dictfetchone()
        return id_dict.get('id')

    def insertRel(self, cr, uid, quant_id, move_ids, context=None):
        for move_id in move_ids:
            cr.execute("""INSERT INTO stock_quant_move_rel (quant_id, move_id) VALUES(%s,%s)
                                                                     """ % (quant_id, move_id))
        return True

    def GetInternalStock(self, cr, uid, ids, context=None):
        # 全部先入库 去除销售退货入库部分
        child_location_dict = {}
        model_data_obj = self.pool.get('ir.model.data')
        picking_type_in = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'stock.picking_type_in')
        cr.execute("""
        select move.id, move.product_id, move.product_uom_qty,
               move.location_dest_id, move.origin_returned_move_id,
                move.date, move.location_id,picking_id
          from stock_move move LEFT JOIN stock_location location
          on move.location_id = location.id
          LEFT JOIN product_product pp ON pp.id=move.product_id
          LEFT JOIN product_template pt ON pp.product_tmpl_id=pt.id
          where move.state='done'
          and move.location_dest_id!=move.location_id and (move.picking_type_id=%s
          and location.usage='supplier' or  move.location_id=5) and pt.type!='service'
           and move.product_id!=12511
          ORDER BY date
        """%(picking_type_in))  # 儿童手表 去除
        move_err_list = []
        for move in cr.dictfetchall():
            child_location = child_location_dict.setdefault(move.get('location_id'),
                self.pool.get('stock.location').get_child_location_ids(cr, uid, move.get('location_id'), context=context))
            return_move = self.get_quant_ids(cr, uid, ids, move, child_location,context=context)
            if return_move:
                move_err_list.append(return_move)
        cr.execute("""
            select move.id, move.product_id, move.product_uom_qty,
                   move.location_dest_id, move.origin_returned_move_id,
                    move.date, move.location_id,move.picking_id
              from stock_move move LEFT JOIN stock_location location  on move.location_id = location.id
               LEFT JOIN product_product pp ON pp.id=move.product_id
                LEFT JOIN product_template pt ON pp.product_tmpl_id=pt.id
              where  move.state='done'
              and move.location_dest_id!=move.location_id
              and location.usage !='supplier' and pt.type!='service' and move.product_id!=12511
              and  move.location_id!=5
              ORDER BY date
            """)  # 儿童手表 去除
        move_err_list = []
        for move in cr.dictfetchall():
            child_location = child_location_dict.setdefault(move.get('location_id'),
                                                            self.pool.get('stock.location').get_child_location_ids(cr, uid, move.get('location_id'), context=context))
            return_move = self.get_quant_ids(cr, uid, ids, move, child_location,context=context)
            if return_move:
                move_err_list.append(return_move)

    def recreate_stock_quant(self, cr, uid, ids, context=None):
        self.GetInternalStock(cr, uid, ids, context=context)
        return True

    def send_stock_up_notify(self, cr, uid, ids, context=None):
        self.pool.get('stock.picking').remind_delivery(cr, uid, SUPERUSER_ID)
        return True

    def correct_sale_line_profit(self, cr, uid, ids, context=None):
        cr.execute("""
            SELECT sol.ID AS line_id FROM sale_order_line sol
            WHERE sol.cost_price>0 and ROUND(gross_profit_rate::numeric, 2)<>  ROUND(((price_unit - cost_price*((1+cost_tax)))/(cost_price*(1+cost_tax)) * 100)::numeric, 2)
                and sol.shipped_qty = sol.product_uom_qty
            ORDER BY sol.order_id
        """)
        line_ids = cr.dictfetchall()
        line_ids = [line.get('line_id') for line in line_ids]
        self.pool.get('sale.order.line').gross_profit_rate_type(cr, uid, line_ids, context=context)
        return True

    def model_create_wiki_file(self, model, file_name, field_note, file_path):
        file_dir = "%s/%s" % ( file_path,model)
        file_dir = unicode(file_dir, "utf-8")
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)
        with open("%s/%s.markdown" %(file_dir, file_name), 'w') as file:
            file.write("页面显示字段名 %s 类型 %s 数据库字段名 %s" % (field_note.string, field_note._type, file_name))
        return True

    def create_wiki(self, cr, uid, ids, context=None):
        # model_names = ['apply.delivery', 'sale.order', 'product.card.line', "purchase.order",
        #                "official.invoice", "stock.change", "mrp.bom"]
        for quant_row in self.browse(cr, uid, ids, context=context):
            for model_name in self.pool.get('ir.model').search_read(cr, uid, [], ['model'], context=context):
                model_obj = self.pool.get(model_name.get('model'))
                if hasattr(model_obj, '_table') and  hasattr(model_obj, '_columns'):
                    [self.model_create_wiki_file(model_obj._table, field_name, field_model, quant_row.file_path)
                    for field_name, field_model in model_obj._columns.iteritems() if field_name not in
                        ['id', 'create_date', 'write_data', 'create_uid', 'write_uid']]

    def recompute_gross_profit(self, cr, uid, ids, context=None):  #从采购单角度重算毛利
        cr.execute("select id, price_unit from purchase_order_line")
        all_price_dict = cr.dictfetchall()
        line_obj = self.pool.get('purchase.order.line')
        for price_dict in all_price_dict:
            line_obj.write(cr, uid, price_dict.get('id'),
                           {'price_unit': price_dict.get('price_unit')}, context=context)
        return True

    def invoice_write_back_purchase_line(self, cr, uid, ids, context=None):
        ac_ids = self.pool.get('account.invoice').search(cr, uid, [('state', 'not in', ('cancel', 'draft'))],
                                                context=context)
        cr.execute("""select  pol.id as purchase_line_id,ail.price_unit as unit_price
                      from account_invoice_line ail
                      LEFT JOIN purchase_order_line pol on ail.purchase_line_id=pol.id
                      where pol.price_unit!=ail.price_unit and pol.state!='cancel'
                       and ail.price_unit!=0  and ail.invoice_id in %s
                       """ % str(ac_ids).replace('[', '(').replace(']', ')'))
        all_price_dict = cr.dictfetchall()
        for pol_price_dict in all_price_dict:
            cr.execute("update purchase_order_line set price_unit=%s WHERE id=%s " %
                       (pol_price_dict.get('unit_price'), pol_price_dict.get('purchase_line_id')))
        return True

    def getMonthFirstDayAndLastDay(self, year=None, month=None):
        """
        :param year: 年份，默认是本年，可传int或str类型
        :param month: 月份，默认是本月，可传int或str类型
        :return: firstDay: 当月的第一天，datetime.date类型
                  lastDay: 当月的最后一天，datetime.date类型
        """

        if year:
            year = int(year)
        else:
            year = currency_data.date.today().year

        if month:
            month = int(month)
        else:
            month = currency_data.date.today().month
        # 获取当月第一天的星期和当月的总天数
        firstDayWeekDay, monthRange = calendar.monthrange(year, month)
        # 获取当月的第一天
        firstDay = currency_data.date(year=year, month=month, day=1)
        lastDay = currency_data.date(year=year, month=month, day=monthRange)
        return firstDay, lastDay

    def get_all_punch_record(self, cr, uid, ids, context=None):
        for record_row in self.browse(cr, uid, ids, context):
            month = False
            year = False
            some_end_date = False
            some_start_date = False
            if record_row.super_date:
                month = record_row.super_date[5:7]
                year = record_row.super_date[0:4]
            first_day, last_day = self.getMonthFirstDayAndLastDay(year, month)
            if record_row.super_date:
                some_end_date = "%s 23:59:59" % last_day
                some_start_date = "%s 00:00:00" % first_day
            self.pool.get("weixin.punch").get_today_punch_record(cr, uid, some_start_date=some_start_date,
                                                                 some_end_date=some_end_date, context=context)
        return True

    def create_department(self, cr, uid, ids, context=None):
        wxappaddresslist = WxAppContact()
        all_record = wxappaddresslist.wxapi.add_department(u'测试部门', parentid='1')

    def get_all_approval(self, cr, uid, ids, context=None):
        for record_row in self.browse(cr, uid, ids, context):
             self.pool.get('weixin.approval').get_all_approval_data(cr, uid, input_start_time = record_row.super_start_date,
                                                                    input_end_time=record_row.super_end_date, context=context)

    def test_summary_all(self, cr, uid, ids, context=None):
        self.pool.get('product.card.delivery.stat.day').refresh_new(cr, uid, context=context)

    def stock_change_supply(self, cr, uid, ids, context=None):
        change_obj = self.pool.get('stock.change')
        quant_obj = self.pool.get('stock.quant')
        change_ids = change_obj.search(cr, uid, [('state', '=', 'done')], context=context)
        for change_row in change_obj.browse(cr, uid, change_ids, context=context):
            cost = self.pool.get('stock.change').get_out_line_cost(cr, uid, change_row, context=context)
            quant_obj.change_stock_change_cost(cr, uid, change_row, cost, context=context)
        return True

    def create_annual(self, cr, uid, ids, context=None):
        self.pool.get('hr.employee').create_employee_annual(cr, uid, context=context)

class stock_quant(osv.osv):
    _inherit = 'stock.quant'
    _columns = {
        'prod_spec': fields.char(u'型号备注', copy=True),
        'so_id': fields.many2one('sale.order', u'销售订单锁定', copy=False, select=True),
        'so_line_id': fields.many2one('sale.order.line', u'销售订单行锁定', copy=False, select=True),
        'expiration_date': fields.date(u'过期时间', copy=True)
    }

    def init(self, cr):
        cr.execute("""
                INSERT INTO "stock_picking"("id","origin","create_date","date_done","write_uid","partner_id",
                "priority","backorder_id","picking_type_id","move_type","message_last_post","company_id","note","state",
                "owner_id","create_uid","min_date","write_date","date","name","recompute_pack_op","max_date","group_id",
                "invoice_state","reception_to_invoice","po_id","so_id","origin_picking_id","delivery_user_id",
                "print_state","invoice_id","delivery_state","delivery_done_time","delivery_start_time",
                "shipping_type_sale","express_type_id","is_card","copy_state",
                "planned_delivery_date")
                SELECT
                    -1,NULL,E'2017-06-13 09:00:00',E'2017-06-13 09:00:00',1,NULL,E'1',NULL,3,E'direct',NULL,1,
                    E'',E'done',NULL,1,E'2016-07-26 08:20:40',E'2016-07-25 08:04:14.671107',
                    E'2016-07-25 08:00:26',E'ZC\\\\INT\\\\00000',FALSE,E'2016-07-26 08:20:40',NULL,E'none',FALSE,NULL,NULL,
                    NULL,NULL,FALSE,NULL,E'waiting_delivery',NULL,NULL,NULL,NULL,FALSE,NULL,NULL
                WHERE
                NOT EXISTS (
                SELECT id FROM stock_picking WHERE id = -1 );

                INSERT INTO "stock_move"("id","origin","product_uos_qty","create_date","move_dest_id",
                "product_uom","price_unit","product_uom_qty","company_id","date","product_qty","product_uos",
                "location_id","priority","picking_type_id","partner_id","note","state","origin_returned_move_id",
                "product_packaging","date_expected","procurement_id","name","create_uid","warehouse_id","inventory_id",
                "partially_available","propagate","restrict_partner_id","procure_method","write_uid","restrict_lot_id",
                "group_id","product_id","split_from","picking_id","location_dest_id","write_date","push_rule_id",
                "rule_id","invoice_state","purchase_line_id","raw_material_change_id","consumed_for",
                "raw_material_production_id","production_id","expected_delivery_time","prod_spec","is_direct_delivery",
                "temp_incomed_qty","temp_delivered_qty","change_id","so_change_id")
                SELECT
                  -1,NULL,0,E'2016-07-25 08:01:10.513981',NULL,1,0,0,1,E'2016-07-25 08:04:15',0,NULL,12,E'1',3,
                  NULL,NULL,E'done',NULL,NULL,E'2016-07-26 08:20:40',NULL,E'service',1,NULL,NULL,FALSE,TRUE,NULL,
                  E'make_to_stock',1,NULL,NULL,1,NULL,-1,12,E'2016-07-25 08:04:14.671107',NULL,NULL,E'none',
                  NULL,NULL,NULL,NULL,NULL,NULL,NULL,FALSE,NULL,NULL,NULL,NULL
                WHERE
                NOT EXISTS (
                SELECT id FROM stock_move WHERE id = -1 );

        """)

    # 解决库存锁定后出库是不按锁定的quant 进行出库/
    def _quants_get_order(self, cr, uid, location, product, quantity, domain=[], orderby='in_date', context=None):
        orderby = 'so_id desc,%s' % orderby
        res = super(stock_quant, self)._quants_get_order(cr, uid, location, product, quantity,
                                                         domain=domain, orderby=orderby, context=context)
        return res

    def create(self, cr, uid, vals, context=None):
        if vals.get('qty') and vals.get('qty') < 0:
            raise Exception(u'建立了负数库存')
        res_id = super(stock_quant, self).create(cr, uid, vals, context=context)
        return res_id

    def write(self, cr, uid, ids, vals, context=None):
        """
        1.设置qty的负数检查
        2.当quant的成本变化时，同时修改quant 对应的销售订单的成本价，或者 转换单对应入库的quant的成本价
        :return:
        """
        if vals.get('qty') and vals.get('qty') < 0:
            raise Exception(u'写入了负数库存')
        return_vals = super(stock_quant, self).write(cr, uid, ids, vals, context=context)
        if vals.get('cost'):
            self.change_sale_line_cost(cr, uid, ids, context=context)
        return return_vals

    def change_sale_line_cost(self, cr, uid, ids, context=None):
        """
        采购变更价格 然后改变对应的入库的quant的成本价2. 然后如果有销售订单行关联就计算对应的销售订单成本价并回写
        """
        move_obj = self.pool.get('stock.move')
        mod_obj = self.pool.get('ir.model.data')
        product_location_id = mod_obj.get_object_reference(cr, uid, 'osbzr_stock_change_ext', 'manufacturing_picking_type')[1]
        for quant_row in self.browse(cr, uid, ids, context=context):
            history_move_ids = [move.id for move in quant_row.history_ids]
            outgoing_picking_type_id = self.pool.get('stock.picking.type').search(
                cr, uid, [('code', '=', 'outgoing')], context=context)[0]
            move_id = move_obj.search(cr, uid, [('id', 'in', history_move_ids),
                                                ('picking_type_id', '=', outgoing_picking_type_id)],
                                               order='date desc', limit=1, context=context)
            if move_id:
                move_obj.update_move_sale_cost(cr, uid, move_id, context=context)
            else:
                move_id = move_obj.search(cr, uid, [('id', 'in', history_move_ids),
                                                    ('picking_type_id', '=', product_location_id)],
                                                    order='date desc', limit=1, context=context)
                if move_id:
                    self.change_stock_change_quant(cr, uid, move_id, context=context)
        return True

    def change_stock_change_cost(self, cr, uid, change_row, cost, context=None):
        """当库存模块"""
        for line in change_row.move_in_lines:
            line.write({'price_unit': cost/line.product_uom_qty})
            line.quant_ids.write({'cost': cost/line.product_uom_qty})
        return True

    def change_stock_change_quant(self, cr, uid, move_id, context=None):
        move_obj = self.pool.get('stock.move')
        move_row = move_obj.browse(cr, uid, move_id, context=context)
        if move_row.raw_material_change_id:
            cost = self.pool.get('stock.change').get_out_line_cost(cr, uid, move_row.raw_material_change_id, context=context)
            self.change_stock_change_cost(cr, uid, move_row.raw_material_change_id, cost, context=context)
        return True

    def quants_unreserve(self, cr, uid, move, context=None):
        related_quants = [x.id for x in move.reserved_quant_ids if x.so_id]
        super(stock_quant, self).quants_unreserve(cr, uid, move, context=context)
        if related_quants: # 取消保留后 ，如果原来库存 被销售 保留的话，取消保留后继续被销售库存锁定
            self.write(cr, SUPERUSER_ID, related_quants, {'reservation_id': -1}, context=context)
        if move.reserved_quant_ids and ((not context.get('is_stock_picking_out') and move.so_change_id) or context.get('is_do_unreserve')):
            move.so_change_id.write({'po_id': False})
            # so_change_id 主要針對卡券提货记录（逻辑上还是有缺陷不能适应大部分订单， move 上无法记录多个change_ids 和                 #
            # 2. change_id 表上的order_line 字段可能确失导致不对， 已处理的销售订单行删除，并添加一个相同数量产品的行，# ）

    def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None):
        res = super(stock_quant, self).move_quants_write(cr, uid, quants, move, location_dest_id,
                                                         dest_package_id, context=context)
        self.write(cr, SUPERUSER_ID,  [q.id for q in quants], {'so_id': False, 'so_line_id': False}, context=context)
        return res

    def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False,
                      force_location_from=False, force_location_to=False, context=None):
        '''Create a quant in the destination location and create a negative quant in the source location if it's an internal location.
        '''
        if context is None:
            context = {}
        price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context)
        location = force_location_to or move.location_dest_id
        rounding = move.product_id.uom_id.rounding
        vals = {
            'product_id': move.product_id.id,
            'location_id': location.id,
            'qty': float_round(qty, precision_rounding=rounding),
            'cost': price_unit,
            'prod_spec': move.prod_spec,
            'expiration_date': move.expiration_date,
            'history_ids': [(4, move.id)],
            'in_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            'company_id': move.company_id.id,
            'lot_id': lot_id,
            'owner_id': owner_id,
            'package_id': dest_package_id,
        }

        if move.location_id.usage == 'internal':
            # if we were trying to move something from an internal location and reach here (quant creation),
            # it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals['location_id'] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals['qty'] = float_round(-qty, precision_rounding=rounding)
            negative_vals['cost'] = price_unit
            negative_vals['negative_move_id'] = move.id
            negative_vals['package_id'] = src_package_id
            negative_quant_id = self.create(cr, SUPERUSER_ID, negative_vals, context=context)
            vals.update({'propagated_from_id': negative_quant_id})

        # create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        quant_id = self.create(cr, SUPERUSER_ID, vals, context=context)
        if move.product_id.valuation == 'real_time':
            self._account_entry_move(cr, uid, [quant_id], move, context)
        return self.browse(cr, uid, quant_id, context=context)

    def _quant_split(self, cr, uid, quant, qty, context=None):
        return_val = super(stock_quant, self)._quant_split(cr, uid, quant, qty, context=context)
        if return_val:# 保留后拆分出来的新的订单，继续为销售订单保留 所以要写入销售订单信息
            self.write(cr, uid, return_val.id, {'so_id': quant.so_id and quant.so_id.id,
                              'so_line_id': quant.so_line_id and quant.so_line_id.id}, context=context)
        return return_val

    def unlock_quant(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'reservation_id': False, 'so_id': False, 'so_line_id': False}, context=context)
        return True


class choose_location_wizard(osv.osv_memory):
    _name = 'choose.location.wizard'

    _columns = {
        'move_source_location_id':fields.many2one('stock.location', string='stock_move父级库位'),
        'location_id': fields.many2one('stock.location', string='库位'),
        'move_id': fields.many2one('stock.move', u'对应的stock_move 的记录id'),
        'is_need_category_expiration': fields.boolean(string='分类', store=True),
        'note': fields.char(u'单据备注'),
        'expiration_date': fields.date(u'过期时间'),
        'line_note': fields.char(u'行备注'),
    }

    def _set_location_id(self,cr,uid,context=None):
        move_row = self.pool.get('stock.move').browse(cr, uid, context.get('active_id'), context=context)
        if move_row:
            return move_row.location_dest_id.id

    def _set_line_note(self, cr, uid, context=None):
        move_row = self.pool.get('stock.move').browse(cr, uid, context.get('active_id'), context=context)
        if move_row:
            return move_row.note

    def _set_note(self, cr, uid, context=None):
        move_row = self.pool.get('stock.move').browse(cr, uid, context.get('active_id'), context=context)
        if move_row: #purchase_line_id', 'so_line_id'
            return move_row.purchase_line_id and move_row.purchase_line_id.order_id.notes or \
                   move_row.so_line_id.order_id.note

    def set_is_need_category_expiration(self, cr, uid, context=None):
        move_row = self.pool.get('stock.move').browse(cr, uid, context.get('active_id'), context=context)
        if move_row and move_row.product_id and move_row.product_id.categ_id \
                and '食品' in move_row.product_id.categ_id.display_name and \
                        move_row.picking_id.picking_type_code == 'incoming':
            return True
        else:
            return False

    _defaults = {
        'move_id': lambda s, c, u, ctx: ctx.get('active_id'),
        'move_source_location_id': _set_location_id,
        'location_id': _set_location_id,
        'note': _set_note,
        'is_need_category_expiration': set_is_need_category_expiration,
        'line_note': _set_line_note,
    }

    def confirm_choose_move_location_id(self, cr, uid, ids, context=None):
        for location_wizard in self.browse(cr, uid, ids, context=context):
            self.pool.get('stock.move').write(cr, uid, location_wizard.move_id.id,
                                              {'location_dest_id': location_wizard.location_id.id,
                                               'expiration_date': location_wizard.expiration_date}, context=context)
            for link_row in location_wizard.move_id.linked_move_operation_ids:
                link_row.operation_id.write({'location_dest_id': location_wizard.location_id.id})
            self.pool.get('stock.move').action_done(cr,uid,location_wizard.move_id.id,context=context)
        return True

class stock_transfer_details(models.TransientModel):
    _inherit = 'stock.transfer_details'
    _description = 'Picking wizard'

    def default_get(self, cr, uid, fields, context=None):
        picking_ids = context.get('active_ids', [])
        active_model = context.get('active_model')
        if not picking_ids or len(picking_ids) != 1:
            # Partial Picking Processing may only be done for one picking at a time
            return False
        assert active_model in ('stock.picking'), 'Bad context propagation'
        picking_id, = picking_ids
        picking = self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context)

        model_data_obj = self.pool.get('ir.model.data')
        location_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'stock.stock_location_stock')
        return_res=super(stock_transfer_details,self).default_get(cr,uid,fields,context=context)
        # if return_res.get('item_ids'): # 默认值功能恢复
        #     if picking.picking_type_code == 'incoming':
        #         [move_data.update({'destinationloc_id': ''}) for move_data in return_res.get('item_ids') if
        #          move_data.get('destinationloc_id') == location_id]
        return return_res


class stock_transfer_details_items(osv.TransientModel):
    _inherit = 'stock.transfer_details_items'
    _description = 'Picking wizard items'

    @api.depends('product_id')
    def _compute_is_need_category_expiration(self):
        for item in self:
            if item.product_id and item.product_id.categ_id and item.product_id.categ_id.display_name \
                 and '食品' in item.product_id.categ_id.display_name and \
                    item.packop_id.picking_id.picking_type_code == 'incoming':
                item.is_need_category_expiration = True

    destinationloc_id = field_new.Many2one('stock.location', 'Destination Location', required=False)
    sourceloc_id = field_new.Many2one('stock.location', 'Source Location', required=False)
    is_need_category_expiration = field_new.Boolean(compute='_compute_is_need_category_expiration', string='分类', store=True)
    expiration_date = field_new.Datetime(u'过期时间')

class stock_location(osv.osv):
    _inherit = 'stock.location'

    def get_child_location_ids(self, cr, uid, location_id, context=None):
        """ 获取仓库 对应的子仓库的所有的 库存数！"""
        location_ids = self.search(cr, uid, [('location_id', '=', location_id)], context=context)
        return_location_ids = [location_id]
        while location_ids:
            return_location_ids += location_ids
            location_ids = self.search(cr, uid, [('location_id', 'in', location_ids)], context=context)
        return return_location_ids


class clear_incoming_quant_wizard(osv.osv_memory):
    _name = 'clear.incoming.quant.wizard'

    _columns = {
        'move_ids': fields.one2many('clear.incoming.quant.wizard.line','wizard_id', string='产品', copy=False)
    }

    def confirm_clear_incoming_quant(self,cr,uid,ids,context=None):
        operation_obj = self.pool.get('stock.pack.operation')
        picking_in_moves = []
        operation_id = []
        picking_out_move=[]
        picking_out_qunat=[]
        for clear_incoming_quant in self.browse(cr,uid,ids,context=context):
            move_picking_dict={}
            for move in clear_incoming_quant.move_ids:
                if move.picking_id.id in move_picking_dict:
                    move_picking_dict[move.picking_id.id].append(str(move.product_id.id))
                else:
                    move_picking_dict.update({move.picking_id.id:[str(move.product_id.id)]})
                picking_in_moves.append(move.move_id)
            for (picking_id,product_ids) in move_picking_dict.items():
                cr.execute('''SELECT sqmr.*, sq.qty, sq.product_id, sm.picking_id, sp.so_id FROM stock_quant sq
                             INNER JOIN stock_quant_move_rel sqmr ON sqmr.quant_id=sq.id
                             INNER JOIN stock_move sm ON sm.id=sqmr.move_id
                             INNER JOIN stock_picking sp ON sp.id=sm.picking_id
                            JOIN(
                            SELECT sqmr.* FROM stock_quant_move_rel sqmr
                            JOIN(
                            SELECT sqmr2.quant_id, sqmr2.move_id FROM stock_move sm
                              INNER JOIN stock_quant_move_rel sqmr2 ON sqmr2.move_id=sm.id
                              WHERE sm.product_id IN (%s) AND sm.picking_id=%s
                            ) purchase_quant
                            ON sqmr.quant_id=purchase_quant.quant_id
                            WHERE sqmr.move_id<>purchase_quant.move_id
                            ) sale_quant
                            ON sqmr.quant_id=sale_quant.quant_id
                            AND sp.so_id > 0;
                    ''' % (','.join(product_ids),str(picking_id)))

                for row in cr.fetchall():
                    self.sale_order_line_com(cr, SUPERUSER_ID, row, context=context)

                    operation_id+=operation_obj.search(cr,SUPERUSER_ID,[('picking_id','=',row[4]),
                                                                ('product_id','=',row[3])],context=context)
                    stock_move = self.pool.get('stock.move').browse(cr,SUPERUSER_ID,[row[1]],context=context)
                    if stock_move:
                        stock_move.write({'state':'cancel'})
                    picking_out_move.append(row[1])
                    picking_out_qunat.append(row[0])
                    # stock_move.purchase_line_id.write({'incomed_qty':stock_move.purchase_line_id.incomed_qty-row[2]
                                                         # ,'delivered_qty':stock_move.purchase_line_id.delivered_qty-row[2]})
        if len(set(picking_out_move)) == 1:
            cr.execute("delete from stock_move where id=%s"%(picking_out_move[0]))
        elif len(set(picking_out_move)) > 1:
            cr.execute("delete from stock_move where id in (%s)" % (','.join(map(str,list(set(operation_id))))))
        cr.execute("delete from stock_quant where id=%s"%(row[0]))

        if len(operation_id)==1:
            cr.execute("delete from stock_pack_operation where id=%s" % (operation_id[0]))
        elif len(operation_id)>1:
            cr.execute("delete from stock_pack_operation where id in (%s)" % (','.join(map(str,operation_id))))

        for move in  picking_in_moves:
            operation_id = operation_obj.search(cr, SUPERUSER_ID, [('picking_id', '=', move.picking_id.id),
                                                                   ('product_id', '=', move.product_id.id)], context=context)
            if len(operation_id)==1:
                cr.execute("delete from stock_pack_operation where id=%s" % (operation_id[0]))
            elif len(operation_id) > 1:
                cr.execute("delete from stock_pack_operation where id in (%s)" % (','.join(map(str, operation_id))))
            move.write({'state':'cancel'})
            quant_id = self.pool.get('stock.quant').search(cr,uid,[('negative_move_id','=',move.id)],context=context)
            if quant_id:
                cr.execute("delete from stock_quant where id=%s" % (quant_id[0]))
            cr.execute("delete from stock_move where id=%s" % (move.id))
        return True

    def sale_order_line_com(self, cr, uid, row, context=None):
        order_line_obj = self.pool.get('sale.order.line')
        order_line_id = order_line_obj.search(cr, uid, [('product_id', '=', row[3]), ('order_id', '=', row[5])],
                                              context=context)
        order_line = order_line_obj.browse(cr, uid, order_line_id, context=context)[0]
        order_line.write(
            {'delivered_qty': order_line.delivered_qty - row[2], 'shipped_qty': order_line.shipped_qty - row[2]})

    def _set_move_ids(self,cr,uid,context=None):
        return_vals = []
        for stock_picking in self.pool.get('stock.picking').browse(cr,uid,context.get('active_ids'),context=context):
            for move in stock_picking.move_lines:
                return_vals.append([0,False,{'move_id':move.id,'product_id':move.product_id.id,
                                             'product_qty':move.product_uom_qty,'prod_model':move.prod_model
                                             ,'picking_id':stock_picking.id}])
        return return_vals
    _defaults={
        'move_ids':_set_move_ids,
    }

class clear_incoming_quant_wizard_line(osv.osv_memory):
    _name = 'clear.incoming.quant.wizard.line'

    _columns = {
        'wizard_id':fields.many2one('clear.incoming.quant.wizard',string='主表关联'),
        'move_id': fields.many2one('stock.move', string='库存移动行'),
        'product_id':fields.many2one('product.product',string='产品'),
        'product_qty': fields.float( string='数量'),
        'prod_model': fields.char(string='产品型号'),
        'picking_id':fields.many2one('stock.picking',u'入库单id')
    }

class batch_stock_incoming_wizard(osv.osv_memory):
    # #1060 产品入库tree页面增加一个向导“批量入库”，弹出页面选择库位后把选择的记录入库 [置顶] [Issue开启]
    _name = 'batch.stock.incoming.wizard'
    _columns = {
        'domain_location_id':fields.many2one('stock.location',u'库位'),
        'location_id':fields.many2one('stock.location',u'库位',readonly=False,domain="['|',('location_id','=',domain_location_id),('id','=',domain_location_id)]")
    }

    def _set_wizard_message(self,cr,uid,context=None):
        move_obj = self.pool.get('stock.move')
        stock_location_id =  False
        po_list = []
        for move_row in move_obj.browse(cr,uid,context.get('active_ids'),context=context):

            if not stock_location_id:
                stock_location_id = move_row.location_dest_id.id
                parent_location_id = move_row.location_dest_id.location_id.id
                po_list.append(move_row.origin  or "无源单据")
            elif (stock_location_id==move_row.location_dest_id.id or parent_location_id == move_row.location_dest_id.id):
                po_list.append(move_row.origin or "无源单据")
            else:
                raise osv.except_osv(
                    u'错误', u"采购单%s的入库库位,与其他采购单（%s）的库位不一致!"%(move_row.origin,'、'.join(po_list)))
        return stock_location_id

    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
        res = super(batch_stock_incoming_wizard, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type,
                                                                       context=context,
                                                                       toolbar=toolbar, submenu=submenu)
        self._set_wizard_message(cr,uid,context=context)
        return res

    def batch_stock_incoming(self,cr,uid,ids,context=None):
        move_obj = self.pool.get('stock.move')
        [move_obj.action_done(cr, uid, move_id, context=context) for move_id in context.get('active_ids')]
        return True

    _defaults = {
        'domain_location_id':_set_wizard_message,
    }